from datetime import date,timedelta
import calendar


class DayCounter:
	def dayCount(self, start_date, end_date):
		#print "DayCounter::dayCount"	
		return (end_date-start_date).days

	def yearFraction(self, start_date, end_date):
		raise NotImplementedError

		
class Actual365Fixed(DayCounter):
	def yearFraction(self, start_date, end_date):
		d1 = start_date
		d2 = end_date
		return self.dayCount(d1,d2)/365.0
		
		
class Thirty360(DayCounter):
	def dayCount(self, start_date, end_date):
		#print "Thirty360::dayCount"		
		d1 = start_date
		d2 = end_date
		
		dd1 = d1.day
		dd2 = d2.day
		mm1 = d1.month
		mm2 = d2.month
		yy1 = d1.year
		yy2 = d2.year
			
		if (mm1 == 2 and dd1 > 27):
			dd1 = 30
		if (mm2 == 2 and dd2 > 27):
			dd2 = 30
			
		if dd2 == 31 and dd1 < 30:
			dd2 = 1
			mm2=mm2+1
		
		return 360*(yy2-yy1) + 30*(mm2-mm1-1) + max(0,30-dd1) + min(30,dd2)
		
	def yearFraction(self, start_date, end_date):
		d1 = start_date
		d2 = end_date
		return self.dayCount(d1,d2)/360.0
		
		
class ActualActual(DayCounter):
	def yearFraction(self, start_date, end_date):
		d1 = start_date
		d2 = end_date
		
		if (d1 == d2):
			return 0.0

		if (d1 > d2):
			return -yearFraction(d2,d1)

		y1 = d1.year
		y2 = d2.year
		
		dib1 = 366.0 if calendar.isleap(y1) else 365.0
		dib2 = 366.0 if calendar.isleap(y2) else 365.0

		sum = y2 - y1 - 1        
		sum += self.dayCount(d1, date(y1+1,1,1))/dib1;
		sum += self.dayCount(date(y2,1,1),d2)/dib2;
		return sum;
		
		
def get_daycounter(dc_name):
	if dc_name.upper() == 'ACT/365':
		return Actual365Fixed()
	elif dc_name.upper() == 'ACT/ACT':
		return ActualActual()
	elif dc_name == '30/360':
		return Thirty360()
	else:
		raise RuntimeError(), ("DayCount Not Available, "+dc_name)
		

def UNIT_TEST_daycount():
	dc1 = get_daycounter('ACT/365')
	dc2 = get_daycounter('ACT/ACT')
	dc3 = get_daycounter('30/360')
	
	#ds = date(2012, 6, 1)
	#de = date(2012, 7, 31)
	ds = date(2008, 1, 31)
	de = date(2008, 2, 29)
	
	#print dc1.yearFraction(ds, de)
	#print dc2.yearFraction(ds, de)
	print dc3.yearFraction(ds, de)*12
	
	#print dc1.dayCount(ds, de)
	#print dc2.dayCount(ds, de)
	print dc3.dayCount(ds, de)		